﻿#pragma once

#include <iostream>
#include <string>
#include "lix/text.h"
#include "lix/chunk/fixed_allocator.h"
#include "lix/character.h"
#include "lix/char_sequence.h"
#include "lix/xml/Node.h"
#include "lix/xml/document.h"

namespace lix{
	namespace xml{



class NodeAllocator : public IDocumentLocalAllocator {
public:
	enum { kNodeSize = Node::kNodeSize };

	NodeAllocator();
	~NodeAllocator();

	bool create();



	Node* allocateNode( NodeType type );
	void deallocateNode( Node* node );

	// テキストノードを作る
	Node* createTextNode( const CharSequence& text );

	// Doctype作る。
	Node* createDocType( const CharSequence& name, const CharSequence& value );

	Node* createDeclare( const CharSequence& name, const CharSequence& value );
	Node* createCData( const CharSequence& value );
	
	Node* createAttribute( const CharSequence& name, const CharSequence& value );

private:
	LIX_DEF_COPY_AND_ASSIGN( NodeAllocator );

	virtual size_t getItemSize();
	virtual void* allocateItem();
	virtual void deallocateItem( void* p );

	lix::FixedAllocator< kNodeSize > m_Body;
	void* m_AllocationCache;
};






class Parser {
public:

	
	
	template<typename PRED>
	static const char* findIfNot( const char* pch, PRED pred )
	{
		while( 1 ){
			char ch = *pch;
			if( ch == 0 || ( ! pred(ch)) ){
				return pch;
			}
			++pch;
		}
	}
	template<typename PRED>
	static const char* findIf( const char* pch, PRED pred )
	{
		while( 1 ){
			char ch = *pch;
			if( ch == 0 || ( pred(ch)) ){
				return pch;
			}
			++pch;
		}
	}
	
	template<class T>
	struct EqualTo{
		EqualTo( const T& t) : value(t){}
		bool operator()( T t){
			return t == value;
		}
		T value;
	};

	struct IsOneOf{
		IsOneOf( const char* pch) : str(pch){}
		bool operator()( char ch){
			const char* pch = this->str;
			while( *pch ){
				if( *pch == ch){
					return true;
				}
				pch++;
			}
			return false;
		}
		const char* str;
	};




	// xmlの名前として使用できる文字か返す。
	static bool isXmlNameChar( char ch ){
		// これ、ちょっと重い、何とかしたいが、
		if( lix::Character::isAlphaNumeric( ch )){
			return true;
		}
		return ( ch == ':' || ch == '.' || ch == '_' || ch == '-' );
	}
	
	
	// UTF-8 BOM のスキップ
	static void skipBOM( const char*& pch )
	{
		if (static_cast<unsigned char>(pch[0]) == 0xEF && 
			static_cast<unsigned char>(pch[1]) == 0xBB && 
			static_cast<unsigned char>(pch[2]) == 0xBF)
		{
			pch += 3;
		}
	}
	
	// 空白のスキップ
	static void skipWhiteSpace( const char*& pch )
	{
		pch = findIfNot( pch, lix::Character::isWhiteSpace<char> );
	}

	
	// タグの開始を検索
	static const char* findStartOfTag( const char* pch )
	{
		return findIf( pch, EqualTo<char>('<'));
	}
	// タグの開始を検索
	static const char* findEndOfTag( const char* pch )
	{
		return findIf( pch, EqualTo<char>('>'));
	}

	static const char* findOneOf(  const char* pch, const char* finds )
	{	
		return findIf( pch, IsOneOf(finds));
	}

	static const char* findEndOfXmlName( const char* pch )
	{
		return findIfNot( pch, isXmlNameChar );
	}

	
	// ノード種判別
	static NodeType recognizeNodeType( const char* pch );

	// コンストラクタ
	Parser( Node* root_node, NodeAllocator* allocator );

	// パース
	ParseResult parse( const char* pch, uint32_t options = 0);
	
	// ノードのパース	
	ParseResult parseNode(  Node* parent, const char*& pch );
	
	
	ParseResult parseDoctype(  Node* parent, const char*& pch );
	ParseResult parseXmlDeclare(  Node* parent, const char*& pch );
	ParseResult parseCData(  Node* parent, const char*& pch );
	ParseResult parseComment(  Node* parent, const char*& pch );
	ParseResult parseProcInst(  Node* parent, const char*& pch );
	ParseResult parseElement(  Node* parent, const char*& pch );



	ParseResult parseElementBody( Node* node, const char*& pch );

	// 閉じタグのパース
	static bool parseCloseTag( const char*& pch, CharSequence& name );

	// アトリビュートの値のパース
	static bool parseAttributeValue( const char*& pch, CharSequence& value );

	
	uint32_t		   m_ParseOptions;
	std::vector<Node*> m_NodePath;
	NodeAllocator* m_Allocator;
	Node* m_RootNode;

	Node* m_ErrorNode;
};








}} // namespace lix::xml